"""
会员（用户）模块
"""
import os
import uuid
from typing import Optional
from fastapi import Query, Path, Body, Header, UploadFile, File, Depends
from pydantic import BaseModel, Field
from starlette.responses import JSONResponse

from dbs.securedao import SecureDao
from errors import JPGError, FileMaxSizeError
from mainapp import app
from dbs.userdao import UserDao
from dbs.caches import *
from mainapp.schemas import UserInfo, RegistUser, PwdSecure
from settings import STATIC_DIR


@app.get('/login')
async def user_login_get(name: str = Query(..., min_length=4, max_length=20, regex=r'^[a-zA-Z]+$'),
                         pwd: str = Query(..., min_length=3, max_length=20)):
    ret = UserDao().login(name, pwd)
    if ret:
        if not ret['is_active']:
            return {'msg': '当前用户未激活, 请先激活用户', 'url': f'/active/{ret["id"]}'}

        ret['username'] = name
        ret['token'] = uuid.uuid4().hex
        # token和user_id 存到缓存中： 待开发
        save_token(ret['token'], ret['id'])

        return {'msg': '登录成功', 'data': ret}

    return {'msg': '登录失败, 用户名或口令错误', 'user': {
        'name': name,
        'pwd': pwd
    }}


@app.get('/active/{user_id}')
def active_user(user_id: int = Path(..., ge=1)):
    dao = UserDao()
    ret = dao.get_user_active_state(user_id)

    if not ret:
        return {'msg': '激活的用户不存在,请确认你的用户ID'}
    elif ret['is_active']:
        return {'msg': '用户已激活,无须再次激活'}
    else:
        # save('tb_user', id=user_id, is_active=1)
        if dao.active(user_id):
            return {'msg': '用户激活成功，可以正常登录了'}

    return {'msg': '用户激活失败'}


@app.put('/user', description='更新用户', name='更新用户')
async def update_user(token: Optional[str] = Header(..., title='请求头上的Token'),
                      user: UserInfo = Body(...)):
    dao = UserDao()  # 数据库的操作对象
    # 实现业务功能
    # 1. 验证token是否有效
    if not has_token(token):
        return {'msg': '这是一个无效的token,请重新登录'}
    # 2. token有效时，获取对应user_id
    user_id = get_user_id(token)
    if not dao.is_user_active(user_id):
        return {'msg': '当前用户状态异常，请联系管理员'}
    # 3. 获取user_id的数据库中的数据或生成更新数据的条件
    # 将BaseModel请求体的对象转成字典
    # 有可能只更新某一项，exclude_unset=True表示只获取请求体中存在的信息
    update_user = user.dict(exclude_unset=True)
    update_user['id'] = user_id  # 更新SQL语句的条件

    # 4. 调用db模块的save()或update()方法，完成数据库中的数据更新
    flag = dao.update_detail(**update_user)

    return {
        'msg': '用户更新成功' if flag else '用户更新失败'
    }


@app.get('/logout')
async def logout(token: Optional[str] = Header(...)):
    remove_token(token)
    return {'msg': '用户退出成功'}


@app.get('/check/{username}')
async def check_username(username: Optional[str] = Path(..., min_length=6)):
    ret = UserDao().check_user_name(username)
    return {
        'status': ret is None,
        'msg': '用户已存在' if ret else '用户名有效'
    }


@app.post('/regist')
async def regist_user(user: RegistUser = Body(...),
                      friend_code: Optional[str] = Query(None, title='邀请码')):
    # 1. 验证两次口令是否相同
    if user.password != user.password2:
        return JSONResponse({'msg': '两次口令不同相'}, 205)
    # 2. 保存用户信息
    user_info = user.dict(exclude_unset=True)
    user_info.pop('password2')
    # 修改password字段为auth_string
    user_info['auth_string'] = user_info.pop('password')
    user_id = UserDao().save(**user_info)  # 保存之后，返回user_id
    token = uuid.uuid4().hex
    save_token(token, user_id)
    return {'msg': '注册成功', 'token': token,
            'user': {'username': user_info['username'],
                     'nick_name': user_info.get('nick_name', '')
                     }
            }


# 上传用户头像的接口， 要求请求头中包含Token标识用户登录之后的操作
@app.post('/avater')
async def upload_user_avater(token: Optional[str] = Header(...),
                             headimg: UploadFile = File(...)):
    # 1. 验证图片大小和类型
    if headimg.content_type != 'image/jpeg':
        raise JPGError()

    content = await headimg.read()  # 读取是字节数据
    if len(content) > 2 * 1024 * 1024:
        raise FileMaxSizeError()

    # 保存文件
    user_id = get_user_id(token)
    filename = f'avaters/{user_id}.jpg'
    with open(os.path.join(STATIC_DIR, filename), 'wb') as f:
        f.write(content)

    file_url = f'/s/{filename}'  # 静态文件访问的路径
    # 修改数据库
    # update('tb_user', id=user_id, head=file_url)
    UserDao().update_detail(id=user_id, head=file_url)
    return {
        'msg': '用户头像上传成功',
        'path': f'/s/{filename}'
    }


@app.patch('/user')
async def update_user_password():
    # 实现用户的口令修改或找回密码
    pass


async def secure_params(token: str = Header(...),
                        secure: PwdSecure = Body(...)):
    # 获取接口参数获取其它数据
    return {'uid': get_user_id(token), **secure.dict()}


async def get_dao():
    return SecureDao()


class SecureParams:
    def __init__(self,token: str = Header(...), secure: PwdSecure = Body(...)):
        self.dao = SecureDao()
        self.params = {'uid': get_user_id(token), **secure.dict()}


@app.post('/secure')
async def add_pwd_secure(data: SecureParams = Depends()):
    # 添加密码找回的问题及答案
    data.dao.add(**data.params)
    return {'msg': '添加成功', 'data': data.params}


@app.get('/secure')
async def list_pwd_secure(token: str = Header(...)):
    # 查看当前用户下的所有密保问题
    user_id = get_user_id(token)
    data = SecureDao().list(user_id)
    return {'msg': '查询成功', 'total': len(data), 'data': data}


@app.put('/secure/{sid}')
async def put_pwd_secure(params=Depends(secure_params),
                         sid: int = Path(...),
                         dao=Depends(get_dao)):
    # 更新问题及答案
    params['id']=sid
    dao.update(**params)
    return {'msg': 'ok'}


@app.delete('/secure/{sid}')
async def delete_secure(token: str = Header(...),
                        sid: int = Path(...)):
    uid = get_user_id(token)
    if SecureDao().delete(id=sid, uid=uid) > 0:
        return {'msg': '删除成功'}

    return JSONResponse({'msg': '删除失败'}, status_code=500)
